package top.lingkang.finalsql.sql;


import top.lingkang.finalsql.annotation.Id;
import top.lingkang.finalsql.config.SqlConfig;
import top.lingkang.finalsql.constants.IdType;
import top.lingkang.finalsql.error.ResultHandlerException;
import top.lingkang.finalsql.utils.ClassUtils;
import top.lingkang.finalsql.utils.NameUtils;
import top.lingkang.finalsql.utils.TabCache;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author lingkang
 * Created by 2022/4/11
 */
public class ResultHandler {
    private SqlConfig sqlConfig;

    public ResultHandler(SqlConfig config) {
        this.sqlConfig = config;
    }

    public <T> List<T> list(ResultSet resultSet, Class<T> clazz) {
        try {
            List<T> list = new ArrayList<>();
            //获取要封装的javabean声明的属性
            TabCache tabCache = ClassUtils.tabCache.get(clazz);
            //遍历ResultSet
            while (resultSet.next()) {
                T obj = clazz.newInstance();
                //匹配JavaBean的属性,然后赋值
                ResultSetMetaData metaData = resultSet.getMetaData();
                for (int i = 0; i < metaData.getColumnCount(); i++) {
                    String columnLabel = metaData.getColumnLabel(i + 1);// 查询对象的SQL已经把列名转化为驼峰命名了，直接取用
                    Field columnField = ClassUtils.getFieldByFieldName(columnLabel, tabCache);
                    if (columnField != null) {
                        columnField.setAccessible(true);
                        columnField.set(obj, tabCache.getTypeHandler()[i].getResult(resultSet, columnField.getName()));
                    }
                }

                list.add(obj);
            }
            return list;
        } catch (SQLException | IllegalAccessException | InstantiationException e) {
            throw new ResultHandlerException(e);
        }
    }

    public <T> List<T> selectForList(ResultSet result, Class<T> clazz) throws Exception {
        List<T> list = new ArrayList<>();
        for (; ; ) {
            T t = selectForObject(result, clazz);
            if (t == null)
                break;
            list.add(t);
        }
        return list;
    }

    public <T> T selectForObject(ResultSet result, Class<T> entity) throws Exception {
        if (ClassUtils.typeHandler.containsKey(entity)) {
            if (result.next()) {
                return (T) ClassUtils.typeHandler.get(entity).getResult(result, 1);
            }
        } else if (Map.class.isAssignableFrom(entity)) {
            if (result.next())
                return (T) selectForMap(result);
        } else {
            if (result.next()) {
                ResultSetMetaData metaData = result.getMetaData();
                T ins = entity.newInstance();// 实例化对象
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    String columnLabel = metaData.getColumnLabel(i);// 查询非entity对象，列名并未驼峰命名化。下面需要手动转化驼峰
                    Field field = ClassUtils.getField(
                            NameUtils.toHump(columnLabel),
                            ins.getClass().getDeclaredFields()
                    );
                    if (field != null) {
                        field.setAccessible(true);
                        field.set(ins, ClassUtils.setTypeHandler(field.getType()).getResult(result, columnLabel));
                    }
                }
                return ins;
            }
        }
        return null;
    }


    public <T> T oneResult(ResultSet resultSet, Class<T> clazz) {
        try {
            //获取要封装的javabean声明的属性
            TabCache tabCache = ClassUtils.tabCache.get(clazz);
            T entity = clazz.newInstance();// 实例化
            //匹配JavaBean的属性,然后赋值
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 0; i < metaData.getColumnCount(); i++) {
                String columnName = metaData.getColumnLabel(i + 1);
                Field columnField = ClassUtils.getFieldByFieldName(columnName, tabCache);
                if (columnField != null) {
                    columnField.setAccessible(true);
                    columnField.set(entity, tabCache.getTypeHandler()[i].getResult(resultSet, columnField.getName()));
                }
            }

            return entity;
        } catch (Exception e) {
            throw new ResultHandlerException(e);
        }
    }

    public <T> int insert(ResultSet resultSet, T entity) throws SQLException, IllegalAccessException {
        TabCache tabCache = ClassUtils.tabCache.get(entity.getClass());
        if (tabCache.getIdType() == IdType.INPUT) {
            return 1;
        }

        Field idColumn = tabCache.getIdColumnField();
        if (idColumn != null) {
            idColumn.setAccessible(true);

            idColumn.set(entity, ClassUtils.setTypeHandler(idColumn.getType()).getResult(resultSet, 1));
        }
        return 1;
    }

    /**
     * @param resultSet
     * @param entity    不为空
     * @param <T>
     * @return
     * @throws SQLException
     * @throws IllegalAccessException
     */
    public <T> int batchInsert(ResultSet resultSet, List<T> entity) throws SQLException, IllegalAccessException {
        boolean notSetId = false;
        Id id = ClassUtils.getId(entity.get(0).getClass());
        if (id != null && id.value() == IdType.INPUT) {
            notSetId = true;
        }

        int i = 0;
        while (resultSet.next()) {
            if (notSetId) {// 不设置返回 id，适用常见数据库
                continue;
            }
            TabCache tabCache = ClassUtils.tabCache.get(entity.get(i).getClass());
            Field idColumn = tabCache.getIdColumnField();
            if (idColumn != null) {
                idColumn.setAccessible(true);
                idColumn.set(entity.get(i), ClassUtils.setTypeHandler(idColumn.getType()).getResult(resultSet, 1));
            }
            i++;
        }
        return i;
    }

    public Map selectForMap(ResultSet result) throws SQLException {
        Map<String, Object> map = new HashMap<>();
        ResultSetMetaData metaData = result.getMetaData();
        for (int i = 1; i <= metaData.getColumnCount(); i++) {
            map.put(metaData.getColumnLabel(i), result.getObject(i));
        }
        return map;
    }

}
